package net.j4love.mybatis.kit.plugin;

import lombok.Data;
import net.j4love.mybatis.kit.CommonObjectCache;
import net.j4love.mybatis.kit.DatabaseEnum;
import net.j4love.mybatis.kit.MybatisKitException;
import net.j4love.mybatis.kit.executor.Executors;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.BaseExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 分页拦截器
 * @author he peng
 * @date 2018-09-05
 */

@Intercepts({
        @Signature(type = Executor.class ,
                method = "query" ,
                args = {MappedStatement.class , Object.class , RowBounds.class ,
                        ResultHandler.class , CacheKey.class , BoundSql.class})
        , @Signature(type = Executor.class ,
        method = "query" ,
        args = {MappedStatement.class , Object.class , RowBounds.class ,
                ResultHandler.class})}
)
public class PaginationQueryInterceptor implements Interceptor {

    private static final Log LOG = LogFactory.getLog(PaginationQueryInterceptor.class);

    private PaginationQuerySQLRewriter paginationQuerySQLRewriter;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return null;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            Executor executor = (Executor) target;
            Configuration conf = Executors.getConfigurationFromBaseExecutor((BaseExecutor) executor);
            Object databaseType = CommonObjectCache.get(CommonObjectCache.DATABASE_TYPE_KEY);
            if (databaseType == null) {
                try {
                    DatabaseMetaData metaData = conf.getEnvironment().getDataSource().getConnection().getMetaData();
                    databaseType = StringUtils.upperCase(metaData.getDatabaseProductName());
                    CommonObjectCache.put(CommonObjectCache.DATABASE_TYPE_KEY , databaseType);
                } catch (SQLException e) {
                    throw new MybatisKitException(PaginationQueryInterceptor.class.getName() + " mybatis interceptor plugin method error" , e);
                }
            }
            this.paginationQuerySQLRewriter = PaginationQuerySQLRewriters.getPaginationQuerySQLRewriter((String) databaseType);
            return Plugin.wrap(executor , this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }

    @Data
    static class Page {
        private Boolean isCount;
        private Integer startPageNo;
        private Integer pageSize;
    }

    /**
     * 分页 SQL 改写器接口
     */
    interface PaginationQuerySQLRewriter {

        /**
         * 改写原有 SQL 为分页查询的 SQL
         * @param page
         * @param originalSql
         * @return
         */
        String rewriteSqlForPaging(Page page , String originalSql);

        /**
         * 改写原有 SQL 为 COUNT(*) 查询的 SQL
         * @param originalSql
         * @return
         */
        String rewriteSqlForCount(String originalSql);
    }

    static class PaginationQuerySQLRewriters {
        private PaginationQuerySQLRewriters() {}

        public static PaginationQuerySQLRewriter getPaginationQuerySQLRewriter(String databaseType) {
            databaseType = StringUtils.upperCase(databaseType);
            DatabaseEnum databaseEnum = DatabaseEnum.valueOf(databaseType);
            PaginationQuerySQLRewriter rewriter;
            switch (databaseEnum) {
                default : rewriter = new MySQLPaginationQuerySQLRewriter(); break;
                case MYSQL : rewriter = new MySQLPaginationQuerySQLRewriter(); break;
            }
            return rewriter;
        }
    }
}
